package com.ymx.darling.executor.mapping.relation;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.bean.sql.BoundSql;
import com.ymx.darling.bean.sql.Statement;
import com.ymx.darling.dialect.keygenerator.NoneKeyGenerator;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.session.setting.relation.RelationEntity;
import com.ymx.darling.bean.sql.SqlStatement;
import com.ymx.darling.util.ArrayUtil;
import com.ymx.darling.util.ReflectUtil;
import com.ymx.darling.util.StringUtil;
import com.ymx.darling.wrapper.QueryWrapper;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 该类为一个关联字段执行器的抽象类型提供字段查询基础的方法
 * @author 爱Java的小于
 */
public abstract class RelationLoader {
    /**
     * 执行关联字段查询
     * @param statement 主查询的statement
     * @param relationEntity 待查询的属性节点
     * @param resultList 主查询的结果集实例
     */
    public void loader(Statement statement, RelationEntity relationEntity, List<?> resultList) {
        try {
            // 判断当前查询节点存在查询深度 则将深度 - 1
            if (relationEntity.hasDepth()) {
                List<RelationEntity> childQueryList = relationEntity.getQueryPropertyList();
                RelationEntity relationEntityCopy = relationEntity.copy();
                int depthReduceOnce = relationEntity.getDepth() - 1;
                relationEntityCopy.setDepth(depthReduceOnce);
                childQueryList.add(relationEntityCopy);
            }

            // 调用目标加载器的方法加载关联数据
            this.invoke(statement, relationEntity, resultList);

        } catch (Exception exception) {
            RelationFieldInfo fieldInfo = relationEntity.getRelationFieldInfo();
            throw ExceptionFactory.wrapRelationException(
                    statement.getModule().getName(),
                    fieldInfo.getFieldName(),
                    relationEntity.getRelationFieldInfo().getAssociationType(),
                    exception
            );
        }
    }


    /**
     * 执行关联字段查询
     * @param statement 主查询的statement
     * @param relationEntity 待查询的属性节点
     * @param resultList 主查询的结果集实例
     */
    public abstract void invoke(Statement statement, RelationEntity relationEntity, List<?> resultList);


    /**
     * 构建字段查询的Statement
     * @param statement 主查询的statement
     * @param relationEntity 查询的节点实例
     * @param sqlStatement sqlStatement
     * @return Statement
     */
    protected Statement createStatement(Statement statement, RelationEntity relationEntity, SqlStatement sqlStatement) {
        Statement fieldQueryStatement = new Statement();
        BeanInfo beanInfo = relationEntity.getBeanInfo();
        fieldQueryStatement.setUseCache(false); // 执行关联字段的查询不走缓存
        fieldQueryStatement.setLogger(statement.getLogger());
        fieldQueryStatement.setModule(beanInfo.getModule());
        fieldQueryStatement.setRelationEntity(relationEntity);
        fieldQueryStatement.setExecutor(statement.getExecutor());
        fieldQueryStatement.setNamespace(statement.getNamespace());
        fieldQueryStatement.setKeyGenerator(new NoneKeyGenerator());
        fieldQueryStatement.setConfiguration(statement.getConfiguration());
        fieldQueryStatement.setNestedQueryParam(statement.getNestedQueryParam());
        fieldQueryStatement.setBoundSql(new BoundSql(sqlStatement.getSql(), sqlStatement.getParamList()));

        return fieldQueryStatement;
    }


    /**
     * 将子查询的值放入主查询的实体中 用于一对一或一对多关联
     * @param selfBeanInfo 主查询的类型的beanInfo
     * @param relationEntity 查询映射的子节点
     * @param resultList 主查询实体集合
     * @param resultMap 子查询实体集合
     */
    protected void putValueToResult(BeanInfo selfBeanInfo, RelationEntity relationEntity, List<?> resultList, Map<?,?> resultMap) {
        FieldInfo selfFieldInfo = this.getSelfColumn(selfBeanInfo);
        RelationFieldInfo relationFieldInfo = relationEntity.getRelationFieldInfo();
        Field associationField = relationFieldInfo.getField();
        resultList.forEach(result -> {
            Object key = ReflectUtil.getValue(selfFieldInfo.getField(), result);
            Object value = resultMap.get(key);
            // 将子查询实体放入主查询实体
            ReflectUtil.setValue(associationField, result, value);
        });
    }


    /**
     * 创建目标表的QueryWrapper
     * @param selfBeanInfo sb
     * @param targetBeanInfo tb
     * @param resultList resultList
     * @return QueryWrapper
     */
    protected QueryWrapper createTargetTableQueryWrapper(BeanInfo selfBeanInfo, BeanInfo targetBeanInfo, List<?> resultList) {
        FieldInfo selfFieldInfo = this.getSelfColumn(selfBeanInfo);
        Set<Serializable> columnSet = new HashSet<>();
        resultList.forEach(result -> columnSet.add(ReflectUtil.getValue(selfFieldInfo.getField(), result)));

        QueryWrapper queryWrapper = this.createQueryWrapperByColumnValues(columnSet, this.getTargetColumn(targetBeanInfo), this.getTargetTable(targetBeanInfo));
        return queryWrapper.select(this.getSelectColumnsWithTargetClass(targetBeanInfo));
    }


    /**
     * 根据指定条件创建目标表的QueryWrapper
     * @param columns 字段值集合
     * @param targetColumn 目标表字段
     * @param targetTable 目标表
     * @return QueryWrapper
     */
    protected QueryWrapper createQueryWrapperByColumnValues(Set<?> columns, String targetColumn, String targetTable) {
        List<Object> columnList = new ArrayList<>(columns);
        QueryWrapper queryWrapper = new QueryWrapper();
        // 确定构建in或eq
        if (columnList.size() == 1) {
            queryWrapper.eq(targetColumn, columnList.get(0));
        } else {
            queryWrapper.in(targetColumn, columnList);
        }
        queryWrapper.from(targetTable);

        return queryWrapper;
    }


    /**
     * 追加查询条件与排序规则
     * @param queryWrapper qw
     * @param condition where条件
     * @param orderBy 排序
     * @param nestedParam where条件中的参数
     */
    protected void applyConditionAndOrderBy(QueryWrapper queryWrapper, String condition, String orderBy, Map<String, Object> nestedParam) {
        if (StringUtil.isNoNull(condition)) {
            if (Objects.isNull(nestedParam)) {
                queryWrapper.and().whereSegment(condition);
            } else {
                queryWrapper.and().whereSegment(condition, nestedParam);
            }
        }
        // 判断配置了目标表的排序
        if (StringUtil.isNoNull(orderBy)) {
            String orderBySegment = String.format(" ORDER BY %s", orderBy);
            queryWrapper.whereSegment(orderBySegment);
        }
    }


    /**
     * 返回目标实体的表名
     * @param targetBeanInfo 目标实体的BeanInfo
     * @return 返回指定的表名 如果目标实体时vo类型 则 返回vo类中的实体引用类型
     */
    protected String getTargetTable(BeanInfo targetBeanInfo) {
        String tableName = targetBeanInfo.getTableName();
        if (StringUtil.isNull(tableName) && targetBeanInfo.getEntityDefBeanInfo() != null) {
            tableName = targetBeanInfo.getEntityDefBeanInfo().getTableName();
        }

        return tableName;
    }


    /**
     * 判断根据@Relation***中的targetClass属性为参考 如果为Void.class 默认返回selectColumn值
     * 如果为实体类型则检查selectColumns属性并返回
     * @return selectColumns
     */
    protected String[] getSelectColumnsWithTargetClass(BeanInfo currentBeanInfo) {
        String[] defaultSelectColumns = this.getSelectColumns();
        // 判断当没有指定targetClass属性时 默认返回selectColumn指定字段
        if (this.getTargetClass() == Void.class) {
            if (defaultSelectColumns.length != 0) {
                HashSet<String> columnSet = new HashSet<>(ArrayUtil.asList(defaultSelectColumns));
                String targetColumn = this.getTargetColumn(currentBeanInfo);
                if (!columnSet.contains(targetColumn)) {
                    columnSet.add(targetColumn);
                    defaultSelectColumns = columnSet.toArray(new String[0]);
                }
            }
            return defaultSelectColumns;

        } else {
            BeanInfo targetBeanInfo = BeanInfoManager.getBeanInfo(this.getTargetClass());
            String targetColumn = this.getTargetColumn(targetBeanInfo);
            // 检查selectColumns中的字段合法性
            if (defaultSelectColumns.length == 0) {
                throw new EntityException("selectColumns字段未提供targetEntity的字段值");
            }
            if (defaultSelectColumns.length > 1) {
                throw new EntityException("selectColumns字段个数不能超过1个");
            }
            FieldInfo fieldInfo = targetBeanInfo.getFieldMap().get(defaultSelectColumns[0]);
            if (fieldInfo == null) {
                throw new EntityException(String.format("类'%s'中找不到selectColumn中的字段'%s'", targetBeanInfo.getModule().getName(), defaultSelectColumns[0]));
            }
            return ArrayUtil.asList(targetColumn, fieldInfo.getColumn()).toArray(new String[0]);
        }
    }


    /**
     * 根据@Relation***中的targetClass属性为参考 如果为Void.class 返回实体本身
     * 如果为一个实体类型 则根据selectColumns属性获取该实例的字段值
     * @param value 源实体的值
     * @return 目标实体的值
     */
    protected Object getValueWithTargetClass(Object value) {
        if (this.getTargetClass() == Void.class) {
            return value;
        } else {
            String targetColumn = this.getSelectColumns()[0];
            BeanInfo beanInfo = BeanInfoManager.getBeanInfo(value.getClass());
            FieldInfo fieldInfo = beanInfo.getFieldMap().get(targetColumn);
            if (fieldInfo == null) {
                throw new EntityException(String.format("类'%s'中找不到字段'%s'", value.getClass().getName(), targetColumn));
            }
            return ReflectUtil.getValue(fieldInfo.getField(), value);
        }
    }


    protected abstract String[] getSelectColumns();


    protected abstract String getTargetColumn(BeanInfo targetBeanInfo);


    protected abstract FieldInfo getSelfColumn(BeanInfo selfBeanInfo);


    public abstract Class<?> getTargetClass();
}
